package com.example.study.config.redis;

import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.cache.*;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.lang.Nullable;

import java.time.Duration;
import java.util.Map;

/**
 * @author fly_roushan
 * 2023/5/11 16:25
 */
public class CustomizerRedisCacheManager extends RedisCacheManager {
    // 自定义ttl的分隔符
    private static final String DEFAULT_SEPARATOR = "#";
    // 用来保存返回值信息和注解，方便获取所需信息及自定义value的序列化
    private final Map<String, CacheInfo> typeMap;
    // 默认的key的序列化方式
    private static final RedisSerializationContext.SerializationPair<String> DEFAULT_KEY_SERIALIZATION = RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer());

    public CustomizerRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, CacheInfo> typeMap) {
        super(cacheWriter, defaultCacheConfiguration);
        this.typeMap = typeMap;
    }

    @Override
    protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
        if (cacheConfig == null) {
            return super.createRedisCache(name, null);
        }
        // 获取并设置ttl
        Duration ttl = getTtlByName(name);
        if (ttl != null) {
            cacheConfig = cacheConfig.entryTtl(ttl);
        }
        // 设置key前缀，没有key时不使用:
        // 默认生成的key时这样, cacheName::key，很难看，而且会出现空目录
        // 现在有key就是, cacheName:key
        // 没有key就是, cacheName, 不会有额外的冒号
        CacheInfo info = typeMap.get(name);
        if (StringUtils.isNotBlank(info.getCacheable().key())) {
            cacheConfig = cacheConfig.computePrefixWith(DEFAULT_CACHE_KEY_PREFIX);
        } else {
            cacheConfig = cacheConfig.computePrefixWith(cacheName -> cacheName);
        }
        cacheConfig = cacheConfig
                .serializeKeysWith(DEFAULT_KEY_SERIALIZATION)
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new FastJsonRedisSerializer<>(info.getReturnType())));
        // 去除最终生成的key中用来设置ttl的字符
        return super.createRedisCache(name.substring(0, name.indexOf(DEFAULT_SEPARATOR)), cacheConfig);
    }

    private static final CacheKeyPrefix DEFAULT_CACHE_KEY_PREFIX = cacheName -> cacheName + ":";

    private Duration getTtlByName(String name) {
        if (name == null) {
            return null;
        }

        String[] cacheParams = name.split(DEFAULT_SEPARATOR);
        if (cacheParams.length > 1) {
            String seconds = cacheParams[1];
            if (StringUtils.isNotBlank(seconds)) {
                return Duration.ofSeconds(Long.parseLong(seconds));
            }
        }

        return null;
    }
}

